package net.xiduth.algorithms;

/**
 * ISSAC algorithm Seed generator | indirection, shift, accumulate, add, and
 * count
 * 
 * @author Graham
 * 
 *         I've added some documentation (Bart)
 * 
 * @see http://en.wikipedia.org/wiki/ISAAC_(cipher)
 * 
 * @since Nov 1, 2012 V1.1 (Build by Graham)
 */
public class ISAACCipher {

	/**
	 * This ISAAC's size log
	 */
	private final int SIZEL = 8;

	/**
	 * The size of this ISAAC's results
	 */
	public final int SIZE = 1 << SIZEL;

	/**
	 * This ISAAC's pseudorandom lookup
	 */
	public final int MASK = (SIZE - 1) << 2;

	/**
	 * This ISAAC's accumulator
	 */
	private int accumulator;

	/**
	 * This ISAAC's results
	 */
	private int result;

	/**
	 * The cycle within the seed generator
	 */
	private int cycle;

	/**
	 * The internal memory state within this ISAAC
	 */
	private int[] memory = new int[SIZE];

	/**
	 * The ISAAC results
	 */
	private int[] results = new int[SIZE];

	/**
	 * This ISAAC's ratio
	 */
	private final int RATIO = -1640531527;

	/**
	 * Constructs this ISAAC seed generator and initiates this ISAAC. Once this
	 * ISAAC is initiated, a 32 bit key pair can be generated
	 * 
	 * @param seed
	 *            - the generator
	 */
	public ISAACCipher(int[] seed) {
		for (int i = 0; i < seed.length; i++) {
			results[i] = seed[i];
		}
		init(true);
	}

	/**
	 * Get's the next seed in the isaac results
	 * 
	 * @return - the next seed
	 */
	public int getNextValue() {
		if (cycle-- == 0) {
			ISAAC();
			cycle = SIZE - 1;
		}
		return results[cycle];
	}

	/**
	 * indirections, shifts, accumulates, adds, and counts using an array of 256
	 * four-octet integers as the internal state, writing the results to another
	 * 256 four-octet integer array, from which they are read one at a time
	 * until empty, at which point they are recomputed. The computation consists
	 * of altering i-element with (i - 128)-element, two elements of the state
	 * array found by indirection, an accumulator, and a counter, for all values
	 * of i from 0 to 255. Since it only takes about 19 32-bit operations for
	 * each 32-bit output word, it is very fast on 32-bit computers.
	 */
	public void ISAAC() {
		int i, j, x, y;
		result += ++cycle;
		for (i = 0, j = SIZE / 2; i < SIZE / 2;) {

			x = memory[i];
			accumulator ^= accumulator << 13;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
			x = memory[i];

			accumulator ^= accumulator >>> 6;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
			x = memory[i];

			accumulator ^= accumulator << 2;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
			x = memory[i];

			accumulator ^= accumulator >>> 16;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
		}
		for (j = 0; j < SIZE / 2;) {
			x = memory[i];
			accumulator ^= accumulator << 13;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
			x = memory[i];
			accumulator ^= accumulator >>> 6;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
			x = memory[i];
			accumulator ^= accumulator << 2;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
			x = memory[i];
			accumulator ^= accumulator >>> 16;
			accumulator += memory[j++];
			memory[i] = y = memory[(x & MASK) >> 2] + accumulator + result;
			results[i++] = result = memory[((y >> SIZEL) & MASK) >> 2] + x;
		}
	}

	/**
	 * Initializes the ISAAC.
	 * 
	 * @param flag
	 *            - determines if we should perform a second pass.
	 */
	public void init(boolean flag) {
		int i;
		int a, b, c, d, e, f, g, h;
		a = b = c = d = e = f = g = h = RATIO;
		for (i = 0; i < 4; ++i) {
			a ^= b << 11;
			d += a;
			b += c;
			b ^= c >>> 2;
			e += b;
			c += d;
			c ^= d << 8;
			f += c;
			d += e;
			d ^= e >>> 16;
			g += d;
			e += f;
			e ^= f << 10;
			h += e;
			f += g;
			f ^= g >>> 4;
			a += f;
			g += h;
			g ^= h << 8;
			b += g;
			h += a;
			h ^= a >>> 9;
			c += h;
			a += b;
		}
		for (i = 0; i < SIZE; i += 8) {
			if (flag) {
				a += results[i];
				b += results[i + 1];
				c += results[i + 2];
				d += results[i + 3];
				e += results[i + 4];
				f += results[i + 5];
				g += results[i + 6];
				h += results[i + 7];
			}
			a ^= b << 11;
			d += a;
			b += c;
			b ^= c >>> 2;
			e += b;
			c += d;
			c ^= d << 8;
			f += c;
			d += e;
			d ^= e >>> 16;
			g += d;
			e += f;
			e ^= f << 10;
			h += e;
			f += g;
			f ^= g >>> 4;
			a += f;
			g += h;
			g ^= h << 8;
			b += g;
			h += a;
			h ^= a >>> 9;
			c += h;
			a += b;
			memory[i] = a;
			memory[i + 1] = b;
			memory[i + 2] = c;
			memory[i + 3] = d;
			memory[i + 4] = e;
			memory[i + 5] = f;
			memory[i + 6] = g;
			memory[i + 7] = h;
		}
		if (flag) {
			for (i = 0; i < SIZE; i += 8) {
				a += memory[i];
				b += memory[i + 1];
				c += memory[i + 2];
				d += memory[i + 3];
				e += memory[i + 4];
				f += memory[i + 5];
				g += memory[i + 6];
				h += memory[i + 7];
				a ^= b << 11;
				d += a;
				b += c;
				b ^= c >>> 2;
				e += b;
				c += d;
				c ^= d << 8;
				f += c;
				d += e;
				d ^= e >>> 16;
				g += d;
				e += f;
				e ^= f << 10;
				h += e;
				f += g;
				f ^= g >>> 4;
				a += f;
				g += h;
				g ^= h << 8;
				b += g;
				h += a;
				h ^= a >>> 9;
				c += h;
				a += b;
				memory[i] = a;
				memory[i + 1] = b;
				memory[i + 2] = c;
				memory[i + 3] = d;
				memory[i + 4] = e;
				memory[i + 5] = f;
				memory[i + 6] = g;
				memory[i + 7] = h;
			}
		}
		ISAAC();
		cycle = SIZE;
	}

}